<?php

namespace Mvc;

// 实用工具类
class Util
{
    private static $_config;
    private static $_langs;
    private static $_lang;
    private static $_voice;
    private static $_trans;
    private static $_modules;

    // 配置信息
    public function config()
    {
        if (self::$_config == null) {
            self::$_config = require dirname(__DIR__) . '/config.php';
        }
        $arg = self::$_config;
        for ($i = 0; $i < func_num_args(); $i++) {
            if (isset($arg[func_get_arg($i)]))
                $arg = $arg[func_get_arg($i)];
        }
        return $arg;
    }

    // 语言数组
    public function langs()
    {
        if (self::$_langs == null) {
            $_file = dirname(__FILE__) . '/locale/Lang.php';
            if (is_file($_file)) {
                self::$_langs = require $_file;
            } else {
                self::$_langs = [];
            }
        }
        return self::$_langs;
    }

    // 获取语言
    public function lang()
    {
        if (self::$_lang == null) {
            $pathInfo = $this->pathInfo();
            $langs = $this->langs();
            $_lang = strstr(ltrim($pathInfo, '/'), '/', true);
            if (!array_filter($langs, function ($item) use ($_lang) {
                return $item['slug'] == $_lang;
            })) {
                $_lang = 'zh-CN';
            }
            self::$_lang = $_lang;
        }
        return self::$_lang;
    }

    // 获取语音
    public function voice()
    {
        if (self::$_voice == null) {
            $langs = $this->langs();
            $_lang = $this->lang();
            $_voice = '';
            $_arrs = array_filter($langs, function ($item) use ($_lang) {
                return $item['slug'] == $_lang;
            });
            if ($_arrs) {
                $_arr = array_shift($_arrs);
                $_voice = $_arr['voice'];
            }
            self::$_voice = $_voice;
        }
        return self::$_voice;
    }

    // 翻译串数组
    public function trans($lang = '')
    {
        if (self::$_trans == null) {
            $_file = dirname(__FILE__) . '/locale/' . ($lang ? $lang : $this->lang()) . '.php';
            if (is_file($_file)) {
                self::$_trans = require $_file;
            } else {
                self::$_trans = [];
            }
        }
        return self::$_trans;
    }

    // 翻译串
    public function tran($str, $lang = '')
    {
        $_trans = $this->trans($lang);
        return isset($_trans[$str]) && $_trans[$str] ? $_trans[$str] : $str;
    }

    // 模块数组
    public function modules()
    {
        if (self::$_modules == null) {
            $_modules = [];
            foreach (glob(dirname(__DIR__) . '/controller/*') as $_source) {
                if (is_dir($_source)) $_modules[] = basename($_source);
            }
            self::$_modules = $_modules;
        }
        return self::$_modules;
    }

    // path info
    public function pathInfo()
    {
        $pathInfo = parse_url(htmlentities($_SERVER['REQUEST_URI']), PHP_URL_PATH);
        return $pathInfo;
    }

    // 获取模块名
    public function module()
    {
        $modules = $this->modules();
        $pathInfo = $this->pathInfo();
        $lang = $this->lang();
        if (0 === strpos(ltrim($pathInfo, '/'), $lang)) {
            $pathInfo = strstr(ltrim($pathInfo, '/'), '/');
        }
        $module = strstr(ltrim($pathInfo, '/'), '/', true);
        if (!in_array($module, $modules)) $module = 'index';
        return $module;
    }

    // 获取控制器
    public function controller()
    {
        $config = $this->config();
        $pathInfo = $this->pathInfo();
        $lang = $this->lang();
        if (0 === strpos(ltrim($pathInfo, '/'), $lang)) {
            $pathInfo = strstr(ltrim($pathInfo, '/'), '/');
        }
        $module = $this->module();
        $requestMethod = $this->requestMethod();
        if (isset($config['rules'][$module][$requestMethod])) {
            foreach ((array)$config['rules'][$module][$requestMethod] as $rule => $_controller) {
                $matches = [];
                if ($rule === $pathInfo || (substr_count($rule, '/') === substr_count($pathInfo, '/') && false !== strpos($rule, '(') && false !== strpos($rule, ')') && preg_match("#$rule#", $pathInfo, $matches))) {
                    return dirname($_controller);
                }
            }
        }
        return false;
    }

    // 获取方法
    public function action()
    {
        $config = $this->config();
        $pathInfo = $this->pathInfo();
        $lang = $this->lang();
        if (0 === strpos(ltrim($pathInfo, '/'), $lang)) {
            $pathInfo = strstr(ltrim($pathInfo, '/'), '/');
        }
        $module = $this->module();
        $requestMethod = $this->requestMethod();
        if (isset($config['rules'][$module][$requestMethod])) {
            foreach ((array)$config['rules'][$module][$requestMethod] as $rule => $_controller) {
                if ($rule === $pathInfo || (substr_count($rule, '/') === substr_count($pathInfo, '/') && false !== strpos($rule, '(') && false !== strpos($rule, ')') && preg_match("#$rule#", $pathInfo, $matches))) {
                    return basename($_controller);
                }
            }
        }
        return false;
    }

    // 查询参数
    public function query()
    {
        $query = [];
        if (!empty($_SERVER['QUERY_STRING'])) {
            parse_str(html_entity_decode($_SERVER['QUERY_STRING']), $query);
        }
        return $query;
    }

    // 获取get请求数据
    public function get($allow = [])
    {
        $data = $this->stripslashes_deep($_GET);
        if (!empty($allow))
            $data = array_intersect_key($data, array_flip((array)$allow));
        if (!is_array($allow))
            return isset($data[$allow]) ? $data[$allow] : false;
        return $data;
    }

    // 获取post请求数据
    public function post($allow = [])
    {
        $data = $this->stripslashes_deep($_POST);
        if (!empty($allow))
            $data = array_intersect_key($data, array_flip((array)$allow));
        if (!is_array($allow))
            return isset($data[$allow]) ? $data[$allow] : false;
        return $data;
    }

    // 获取get|post查询数据
    public function param($allow = [])
    {
        $data = array_merge($_GET, $_POST);
        $data = $this->stripslashes_deep($data);
        if (!empty($allow))
            $data = array_intersect_key($data, array_flip((array)$allow));
        if (!is_array($allow))
            return isset($data[$allow]) ? $data[$allow] : false;
        return $data;
    }

    // 创建路由url
    public function url($segment, $params = [])
    {
        list($url,) = explode('.', $segment);
        if ('/' !== substr($url, -1))
            $url .= ".html";
        if (!empty($params))
            $url .= is_array($params) ? '?' . http_build_query($params) : $params;
        $url = "//" . $_SERVER["SERVER_NAME"] . (isset($_SERVER["SERVER_PORT"]) ? (in_array($_SERVER["SERVER_PORT"], ["80", "443"]) ? "" : ":{$_SERVER["SERVER_PORT"]}") : "") . '/' . ltrim($url, '/');
        if ('admin' !== $this->module())
            $url = "http" . ($this->isSsl() ? "s" : "") . ":" . $url;
        return $url;
    }

    // 判断是否是ssl
    public function isSsl()
    {
        if (isset($_SERVER['HTTPS'])) {
            if ('on' === strtolower($_SERVER['HTTPS']))
                return true;
            if ('1' == $_SERVER['HTTPS'])
                return true;
        } elseif (isset($_SERVER['SERVER_PORT']) && '443' == $_SERVER['SERVER_PORT'])
            return true;
        return false;
    }

    // 获取请求方法
    public function requestMethod()
    {
        if (isset($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) == 'xmlhttprequest')
            $requestMethod = 'ajax';
        else
            $requestMethod = strtolower($_SERVER['REQUEST_METHOD']);
        return $requestMethod;
    }

    // 获取请求路径
    public function requestUrl()
    {
        $requestUrl = "http" . ($this->isSsl() ? "s" : "") . "://" . $_SERVER["SERVER_NAME"] . (isset($_SERVER["SERVER_PORT"]) ? (in_array($_SERVER["SERVER_PORT"], ["80", "443"]) ? "" : ":{$_SERVER["SERVER_PORT"]}") : "") . htmlentities($_SERVER['REQUEST_URI']);
        return $requestUrl;
    }

    // 错误消息
    public function errMsg($_args = [])
    {
        $args = array_merge(['code' => 1, 'msg' => 'Error!'], is_array($_args) ? $_args : ['msg' => $_args]);
        if ($this->isAjax()) {
            echo json_encode($args);
        } else {
            if (!empty($args['msg']))
                echo '<meta charset="UTF-8" /><p>&nbsp;</p><p style="text-align:center;color:#393D49;font-size:20px;">' . $args['msg'] . '</p>';
            if (!empty($args['url']))
                echo '<script>
                        setTimeout(function () {
                            var _ta = window.location;
                            if (self.frameElement && self.frameElement.tagName == "IFRAME") {
                                _ta = parent.window.location;
                            }
                            _ta.href = "' . $args['url'] . '";
                        }, 1000);
                        </script>';
        }
        if (!empty($args['code'])) exit;
    }

    // 二维数组按键重组
    public function formatKey($dataArr, $key = 'id', $column = null)
    {
        $res = [];
        foreach ($dataArr as $data) {
            $res[$data[$key]] = $column ? $data[$column] : $data;
        }
        return $res;
    }

    // 去除反斜杠
    public function stripslashes_deep($value)
    {
        $value = is_array($value) ? array_map([$this, 'stripslashes_deep'], $value) : stripslashes($value);
        return $value;
    }

    // 无限层级子数据
    public function levels($dataArr, $pid = 0, $level = 0)
    {
        $res = [];
        foreach ($dataArr as $data) {
            if ($data['pid'] == $pid) {
                $data['level'] = $level;
                $data['children'] = $this->levels($dataArr, $data['id'], $level + 1);
                $res[] = $data;
            }
        }
        return $res;
    }

    // 无限层级扁平树
    public function trees($dataArr, $pid = 0, $level = 0)
    {
        $res = array();
        foreach ($dataArr as $data) {
            if ($data['pid'] == $pid) {
                $data['level'] = $level;
                $res[] = $data;
                $res = array_merge($res, $this->trees($dataArr, $data['id'], $level + 1));
            }
        }
        return $res;
    }

    // 无限层级子到父平级
    public function parents($dataArr, $id = 0)
    {
        $res = array();
        foreach ($dataArr as $data) {
            if ($data['id'] == $id) {
                $res[] = $data;
                $res = array_merge($res, $this->parents($dataArr, $data['pid']));
            }
        }
        return $res;
    }

    // 无限层级子数据平级
    protected function plates($dataArr, &$res = [])
    {
        foreach ($dataArr as $data) {
            if (isset($data['children'])) {
                $temp = $data['children'];
                unset($data['children']);
                $res[] = $data;
                $this->plates($temp, $res);
            } else {
                $res[] = $data;
            }
        }
        return $res;
    }

    // 图片验证码
    public function captcha($_config = [])
    {
        // 配置
        $config = array_merge([
            // 验证码字符集
            'codeStr' => 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789',
            // 验证码个数
            'codeCount' => 4,
            // 字体大小
            'fontsize' => 16,
            // 验证码的宽度
            'width' => 100,
            // 验证码高度
            'height' => 36,
            // 是否有干扰点?true有,false没有
            'disturbPoint' => true,
            // 干扰点个数,disturbPoint开启后生效
            'pointCount' => 200,
            // 是否有干扰条?true有,false没有
            'disturbLine' => true,
            // 干扰条个数,disturbLine开启后生效
            'lineCount' => 3
        ], $_config);
        //创建画布
        $image = imagecreatetruecolor($config['width'], $config['height']);
        //背景颜色
        $bgColor = imagecolorallocate($image, 255, 255, 255);
        imagefill($image, 0, 0, $bgColor);
        $captcha_code = ''; //存储验证码
        $captchaCodeArr = str_split($config['codeStr']);
        //随机选取4个候选字符
        for ($i = 0; $i < $config['codeCount']; $i++) {
            $fontsize = $config['fontsize'];
            $fontColor = imagecolorallocate($image, rand(0, 120), rand(0, 120), rand(0, 120)); //随机颜色
            $fontContent = $captchaCodeArr[rand(0, strlen($config['codeStr']) - 1)];
            $captcha_code .= $fontContent;
            $_x = $config['width'] / $config['codeCount'];
            $x = ($i * (int)$_x) + rand(5, 10); //随机坐标
            $y = rand(5, 10);
            imagestring($image, $fontsize, $x, $y, $fontContent, $fontColor); //水平地画一行字符串
        }
        !session_id() && session_start();
        $_SESSION['captcha'] = $captcha_code;
        //增加干扰点
        if ($config['disturbPoint']) {
            for ($i = 0; $i < $config['pointCount']; $i++) {
                $pointColor = imagecolorallocate($image, rand(50, 200), rand(50, 200), rand(50, 200));
                imagesetpixel($image, rand(1, 99), rand(1, 29), $pointColor);
            }
        }
        //增加干扰线
        if ($config['disturbLine']) {
            for ($i = 0; $i < $config['lineCount']; $i++) {
                $lineColor = imagecolorallocate($image, rand(80, 220), rand(80, 220), rand(80, 220));
                imageline($image, rand(1, 99), rand(1, 29), rand(1, 99), rand(1, 29), $lineColor);
            }
        }
        //输出格式
        header('Content-Type:image/png');
        imagepng($image);
        //销毁图片
        imagedestroy($image);
    }

    // 产生唯一ID
    public function uniqidReal($length = 13)
    {
        if (function_exists("random_bytes")) {
            $bytes = random_bytes(ceil($length / 2));
        } elseif (function_exists("openssl_random_pseudo_bytes")) {
            $bytes = openssl_random_pseudo_bytes(ceil($length / 2));
        } else {
            throw new \Exception("no cryptographically secure random function available");
        }
        return substr(bin2hex($bytes), 0, $length);
    }

    // 缓存字符串
    public function setCache($name, $value, $expire = 7000)
    {
        $path = __DIR__ . '/cache_str/';
        !is_dir($path) && mkdir($path, 0700);
        $filename = $path . "cache_{$name}.php";
        $json = json_encode(array($name => $value, "expire" => time() + $expire));
        $result = file_put_contents($filename, $json);
        if ($result) {
            return true;
        }
        return false;
    }

    // 获取缓存字符串
    public function getCache($name)
    {
        $path = __DIR__ . '/cache_str/';
        $filename = $path . "cache_{$name}.php";
        if (!is_file($filename)) {
            return false;
        }
        $content = file_get_contents($filename);
        $arr = json_decode($content, true);
        if ($arr['expire'] <= time()) {
            return false;
        }
        return $arr[$name];
    }

    // 删除缓存字符串
    public function delCache($name)
    {
        $path = __DIR__ . '/cache_str/';
        $filename = $path . "cache_{$name}.php";
        if (!is_file($filename)) {
            return true;
        }
        if (unlink($filename)) {
            return true;
        }
        return false;
    }

    // 删除目录
    public function delDir($path, $clean = false)
    {
        if (!is_dir($path)) {
            return false;
        }
        $files = glob($path . '/*');
        if ($files) {
            foreach ($files as $file) {
                is_dir($file) ? $this->delDir($file) : @unlink($file);
            }
        }
        return $clean ? true : @rmdir($path);
    }

    // curl 请求
    public function curl($url = '', $request_data = [], $request_method = 'GET', $request_header = [], $content_type = false)
    {
        $ip = $this->getIp();
        if (false !== strpos($ip, '127.') || false !== strpos($ip, '192.')) $ip = '14.119.104.189';
        $referer = isset($_SERVER["HTTP_REFERER"]) && $_SERVER["HTTP_REFERER"] && !preg_match('/localhost|127\.0\.0\.1/i', $_SERVER["HTTP_REFERER"]) ? $_SERVER["HTTP_REFERER"] : 'https://www.baidu.com/';
        $useragent = (isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] && strlen($_SERVER['HTTP_USER_AGENT']) > 10 ? $_SERVER['HTTP_USER_AGENT'] : 'Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36');
        $header = [
            'Content-type' => 'application/x-www-form-urlencoded',
            'X-Requested-With' => 'XMLHttpRequest',
            'User-Agent' => $useragent,
            'Referer' => $referer,
            'X-Forwarded-For' => $ip,
            'CLIENT-IP' => $ip
        ];
        $keys = array_keys($request_header);
        if (empty($keys) || !is_numeric($keys[0])) {
            $header = array_merge($header, $request_header);
            $request_header = [];
            foreach ($header as $key => $value) {
                $request_header[] = $key . ': ' . $value;
            }
        }
        $ch = curl_init();
        if (!empty($request_data)) {
            if (is_array($request_data)) {
                $request_data = http_build_query($request_data);
            }
            if ($request_method === 'GET') {
                curl_setopt($ch, CURLOPT_HTTPGET, true);
                $url = $url . '?' . $request_data;
            } else {
                if ($request_method === 'POST') {
                    curl_setopt($ch, CURLOPT_POST, true);
                } else {
                    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, $request_method);
                }
                curl_setopt($ch, CURLOPT_POSTFIELDS, $request_data);
            }
        }
        curl_setopt($ch, CURLOPT_URL, $url);
        curl_setopt($ch, CURLOPT_HTTPHEADER, $request_header);
        if (preg_match('/^https:\/\//', $url)) {
            curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 0);
        }
        if (!ini_get('safe_mode') && !ini_get('open_basedir')) {
            curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
        }
        curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
        curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 5);
        curl_setopt($ch, CURLOPT_TIMEOUT, 30);
        $result = curl_exec($ch);
        $mime_type = '';
        if (!curl_errno($ch)) {
            $info = curl_getinfo($ch);
            $mime_type = $info['content_type'];
            if ($info['http_code'] == 301 || $info['http_code'] == 302) {
                list($header, $data) = explode("\r\n\r\n", $result);
                if (preg_match('/Location:(.*?)\n/', $header, $matches)) {
                    curl_setopt($ch, CURLOPT_URL, trim(array_pop($matches)));
                    curl_setopt($ch, CURLOPT_HEADER, false);
                    $result = curl_exec($ch);
                    if (!curl_errno($ch)) {
                        $mime_type = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
                    }
                }
            }
        }
        curl_close($ch);
        return $content_type ? compact('result', 'mime_type') : $result;
    }

    // 获取mimeType类型
    public function mimeType($filename)
    {
        $mime_types = array(
            'apk' => 'application/vnd.android.package-archive',
            '3gp' => 'video/3gpp',
            'ai' => 'application/postscript',
            'aif' => 'audio/x-aiff',
            'aifc' => 'audio/x-aiff',
            'aiff' => 'audio/x-aiff',
            'asc' => 'text/plain',
            'atom' => 'application/atom+xml',
            'au' => 'audio/basic',
            'avi' => 'video/x-msvideo',
            'bcpio' => 'application/x-bcpio',
            'bin' => 'application/octet-stream',
            'bmp' => 'image/bmp',
            'cdf' => 'application/x-netcdf',
            'cgm' => 'image/cgm',
            'class' => 'application/octet-stream',
            'cpio' => 'application/x-cpio',
            'cpt' => 'application/mac-compactpro',
            'csh' => 'application/x-csh',
            'css' => 'text/css',
            'dcr' => 'application/x-director',
            'dif' => 'video/x-dv',
            'dir' => 'application/x-director',
            'djv' => 'image/vnd.djvu',
            'djvu' => 'image/vnd.djvu',
            'dll' => 'application/octet-stream',
            'dmg' => 'application/octet-stream',
            'dms' => 'application/octet-stream',
            'doc' => 'application/msword',
            'dtd' => 'application/xml-dtd',
            'dv' => 'video/x-dv',
            'dvi' => 'application/x-dvi',
            'dxr' => 'application/x-director',
            'eps' => 'application/postscript',
            'etx' => 'text/x-setext',
            'exe' => 'application/octet-stream',
            'ez' => 'application/andrew-inset',
            'flv' => 'video/x-flv',
            'gif' => 'image/gif',
            'gram' => 'application/srgs',
            'grxml' => 'application/srgs+xml',
            'gtar' => 'application/x-gtar',
            'gz' => 'application/x-gzip',
            'hdf' => 'application/x-hdf',
            'hqx' => 'application/mac-binhex40',
            'htm' => 'text/html',
            'html' => 'text/html',
            'ice' => 'x-conference/x-cooltalk',
            'ico' => 'image/x-icon',
            'ics' => 'text/calendar',
            'ief' => 'image/ief',
            'ifb' => 'text/calendar',
            'iges' => 'model/iges',
            'igs' => 'model/iges',
            'jnlp' => 'application/x-java-jnlp-file',
            'jp2' => 'image/jp2',
            'jpe' => 'image/jpeg',
            'jpeg' => 'image/jpeg',
            'jpg' => 'image/jpeg',
            'js' => 'application/x-javascript',
            'kar' => 'audio/midi',
            'latex' => 'application/x-latex',
            'lha' => 'application/octet-stream',
            'lzh' => 'application/octet-stream',
            'm3u' => 'audio/x-mpegurl',
            'm4a' => 'audio/mp4a-latm',
            'm4p' => 'audio/mp4a-latm',
            'm4u' => 'video/vnd.mpegurl',
            'm4v' => 'video/x-m4v',
            'mac' => 'image/x-macpaint',
            'man' => 'application/x-troff-man',
            'mathml' => 'application/mathml+xml',
            'me' => 'application/x-troff-me',
            'mesh' => 'model/mesh',
            'mid' => 'audio/midi',
            'midi' => 'audio/midi',
            'mif' => 'application/vnd.mif',
            'mov' => 'video/quicktime',
            'movie' => 'video/x-sgi-movie',
            'mp2' => 'audio/mpeg',
            'mp3' => 'audio/mpeg',
            'mp4' => 'video/mp4',
            'mpe' => 'video/mpeg',
            'mpeg' => 'video/mpeg',
            'mpg' => 'video/mpeg',
            'mpga' => 'audio/mpeg',
            'ms' => 'application/x-troff-ms',
            'msh' => 'model/mesh',
            'mxu' => 'video/vnd.mpegurl',
            'nc' => 'application/x-netcdf',
            'oda' => 'application/oda',
            'ogg' => 'application/ogg',
            'ogv' => 'video/ogv',
            'pbm' => 'image/x-portable-bitmap',
            'pct' => 'image/pict',
            'pdb' => 'chemical/x-pdb',
            'pdf' => 'application/pdf',
            'pgm' => 'image/x-portable-graymap',
            'pgn' => 'application/x-chess-pgn',
            'pic' => 'image/pict',
            'pict' => 'image/pict',
            'png' => 'image/png',
            'pnm' => 'image/x-portable-anymap',
            'pnt' => 'image/x-macpaint',
            'pntg' => 'image/x-macpaint',
            'ppm' => 'image/x-portable-pixmap',
            'ppt' => 'application/vnd.ms-powerpoint',
            'ps' => 'application/postscript',
            'qt' => 'video/quicktime',
            'qti' => 'image/x-quicktime',
            'qtif' => 'image/x-quicktime',
            'ra' => 'audio/x-pn-realaudio',
            'ram' => 'audio/x-pn-realaudio',
            'ras' => 'image/x-cmu-raster',
            'rdf' => 'application/rdf+xml',
            'rgb' => 'image/x-rgb',
            'rm' => 'application/vnd.rn-realmedia',
            'roff' => 'application/x-troff',
            'rtf' => 'text/rtf',
            'rtx' => 'text/richtext',
            'sgm' => 'text/sgml',
            'sgml' => 'text/sgml',
            'sh' => 'application/x-sh',
            'shar' => 'application/x-shar',
            'silo' => 'model/mesh',
            'sit' => 'application/x-stuffit',
            'skd' => 'application/x-koan',
            'skm' => 'application/x-koan',
            'skp' => 'application/x-koan',
            'skt' => 'application/x-koan',
            'smi' => 'application/smil',
            'smil' => 'application/smil',
            'snd' => 'audio/basic',
            'so' => 'application/octet-stream',
            'spl' => 'application/x-futuresplash',
            'src' => 'application/x-wais-source',
            'sv4cpio' => 'application/x-sv4cpio',
            'sv4crc' => 'application/x-sv4crc',
            'svg' => 'image/svg+xml',
            'swf' => 'application/x-shockwave-flash',
            't' => 'application/x-troff',
            'tar' => 'application/x-tar',
            'tcl' => 'application/x-tcl',
            'tex' => 'application/x-tex',
            'texi' => 'application/x-texinfo',
            'texinfo' => 'application/x-texinfo',
            'tif' => 'image/tiff',
            'tiff' => 'image/tiff',
            'tr' => 'application/x-troff',
            'tsv' => 'text/tab-separated-values',
            'txt' => 'text/plain',
            'ustar' => 'application/x-ustar',
            'vcd' => 'application/x-cdlink',
            'vrml' => 'model/vrml',
            'vxml' => 'application/voicexml+xml',
            'wav' => 'audio/x-wav',
            'wbmp' => 'image/vnd.wap.wbmp',
            'wbxml' => 'application/vnd.wap.wbxml',
            'webm' => 'video/webm',
            'wml' => 'text/vnd.wap.wml',
            'wmlc' => 'application/vnd.wap.wmlc',
            'wmls' => 'text/vnd.wap.wmlscript',
            'wmlsc' => 'application/vnd.wap.wmlscriptc',
            'wmv' => 'video/x-ms-wmv',
            'wrl' => 'model/vrml',
            'xbm' => 'image/x-xbitmap',
            'xht' => 'application/xhtml+xml',
            'xhtml' => 'application/xhtml+xml',
            'xls' => 'application/vnd.ms-excel',
            'xml' => 'application/xml',
            'xpm' => 'image/x-xpixmap',
            'xsl' => 'application/xml',
            'xslt' => 'application/xslt+xml',
            'xul' => 'application/vnd.mozilla.xul+xml',
            'xwd' => 'image/x-xwindowdump',
            'xyz' => 'chemical/x-xyz',
            'zip' => 'application/zip'
        );
        $parts = explode('.', $filename);
        $part = strtolower(array_pop($parts));
        list($ext) = explode('?', $part);
        return isset($mime_types[$ext]) ? $mime_types[$ext] : 'application/octet-stream';
    }

    // API json数据
    public function json($code = 200, $message = '请求成功', $list = [], $total = 0)
    {
        $json = [
            'code' => $code,
            'msg' => $message
        ];
        if (!empty($list)) {
            $json['data'] = $list;
        }
        if (!empty($total)) {
            $json['total'] = $total;
        }
        header("Access-Control-Allow-Origin:*");
        header('Content-type: application/json');
        exit(json_encode($json, 256));
    }

    // 获取Ip
    public function getIp()
    {
        static $ip = '';
        $ip = $_SERVER['REMOTE_ADDR'];
        if (isset($_SERVER['HTTP_CDN_SRC_IP'])) {
            $ip = $_SERVER['HTTP_CDN_SRC_IP'];
        } elseif (isset($_SERVER['HTTP_CLIENT_IP'])) {
            $ip = $_SERVER['HTTP_CLIENT_IP'];
        } elseif (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && preg_match_all('#\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}#s', $_SERVER['HTTP_X_FORWARDED_FOR'], $matches)) {
            foreach ($matches[0] as $xip) {
                if (!preg_match('#^(10|172\.16|192\.168)\.#', $xip)) {
                    $ip = $xip;
                    break;
                }
            }
        }
        if (preg_match('/^([0-9]{1,3}\.){3}[0-9]{1,3}$/', $ip)) {
            return $ip;
        } else {
            return '127.0.0.1';
        }
    }

    // 获取地址
    public function getAddress($ip, $ak = '')
    {
        $ak = (empty($ak)) ? "ACEBZ-FDXWP-WFRDV-VGS5Q-S2Q5K-HQBNA" : $ak;
        $url = "https://apis.map.qq.com/ws/location/v1/ip?ip=$ip&key=$ak";
        $rs = json_decode($this->curl($url), 1);
        if ($rs['status'] == 0) {
            return $rs['result'];
        } else {
            return $rs['message'];
        }
    }

    // 加解密
    public function authCode($string, $operation = 'DECODE', $key = '', $expiry = 0)
    {
        $cKey_length = 4;
        $keyA = md5(substr($key, 0, 16));
        $keyB = md5(substr($key, 16, 16));
        $keyC = $cKey_length ? ($operation == 'DECODE' ? substr($string, 0, $cKey_length) : substr(md5(microtime()), -$cKey_length)) : '';
        $cryptKey = $keyA . md5($keyA . $keyC);
        $key_length = strlen($cryptKey);
        $string = $operation == 'DECODE' ? base64_decode(substr($string, $cKey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0) . substr(md5($string . $keyB), 0, 16) . $string;
        $string_length = strlen($string);
        $result = '';
        $box = range(0, 255);
        $rndKey = array();
        for ($i = 0; $i <= 255; $i++) {
            $rndKey[$i] = ord($cryptKey[$i % $key_length]);
        }
        for ($j = $i = 0; $i < 256; $i++) {
            $j = ($j + $box[$i] + $rndKey[$i]) % 256;
            $tmp = $box[$i];
            $box[$i] = $box[$j];
            $box[$j] = $tmp;
        }
        for ($a = $j = $i = 0; $i < $string_length; $i++) {
            $a = ($a + 1) % 256;
            $j = ($j + $box[$a]) % 256;
            $tmp = $box[$a];
            $box[$a] = $box[$j];
            $box[$j] = $tmp;
            $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
        }
        if ($operation == 'DECODE') {
            if ((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26) . $keyB), 0, 16)) {
                return substr($result, 26);
            } else {
                return '';
            }
        } else {
            return $keyC . str_replace('=', '', base64_encode($result));
        }
    }

    // 判断是否ajax
    public function isAjax()
    {
        return 'ajax' === $this->requestMethod();
    }

    // 判断是否get
    public function isGet()
    {
        return 'get' === $this->requestMethod();
    }

    // 判断是否POST
    public function isPost()
    {
        return 'post' === $this->requestMethod();
    }

    // 判断是否put
    public function isPut()
    {
        return 'put' === $this->requestMethod();
    }

    // 判断是否patch
    public function isPatch()
    {
        return 'patch' === $this->requestMethod();
    }

    // 判断是否options
    public function isOptions()
    {
        return 'options' === $this->requestMethod();
    }

    // 判断是否delete
    public function isDelete()
    {
        return 'delete' === $this->requestMethod();
    }
}
